import React from 'react'
import ReactDOM from 'react-dom'
import cssStyle from './index.module.css'
import arrow from '@/assets/images/arrow-gray.png'
import PropTypes from 'prop-types'
import { Tag } from '@/component'

class TimePickerComponent extends React.PureComponent{
    constructor(props){
        super(props)
        let time
        if(props.value){
            time = props.value.split(':')
        }else{
            time = [0, 0, 0]
        }
        this.state = {
            hour: parseInt(time[0]),
            minute: parseInt(time[1]),
            second: parseInt(time[2])
        }
        this.timer = null
        this.ref = React.createRef()
        this.locationListener = null
    }
    onChange = (value, type) => {
        this.setState({
            [type]: value
        })
    }
    confirm = () => {
        let time = this.state.hour + ':' + this.state.minute + ':' + this.state.second
        this.props.confirm(time, this.props.current)
    }
    clickListener = e => {
        if(!this.ref.current.contains(e.target)){
            this.props.toggle(this.props.current)
        }
    }
    remove = () => {
        this.props.remove()
    }
    locationChange = () => {
        if(this.locationListener){
            window.cancelAnimationFrame(this.locationListener)
            this.locationListener = null
        }
        this.locationListener = window.requestAnimationFrame(() => {
            let { left, top } = this.getLocation()
            this.ref.current.style.top = top + 'px'
            this.ref.current.style.left = left + 'px'
            this.locationChange()
        })
    }
    getLocation = () => {
        let location = this.props.current.getBoundingClientRect()
        let top = location.bottom
        let currentHeight = this.ref.current.children[0].clientHeight
        let bodyHeight = document.documentElement.clientHeight
        if(top + currentHeight > bodyHeight){
            top = top - currentHeight - (location.height) - 2
        }
        return {
            left: location.left,
            top
        }
    }
    show = () => {
        let { left, top } = this.getLocation()
        this.ref.current.style.top = top + 'px'
        this.ref.current.style.left = left + 'px'
        this.ref.current.style.opacity = 1
        this.ref.current.style.height = this.ref.current.children[0].clientHeight + 'px'
        this.locationChange()
        setTimeout(() => {
            window.removeEventListener('click', this.clickListener)
            window.addEventListener('click', this.clickListener)
        }, 300)
    }
    componentDidUpdate(){
        if(this.timer){
            return
        }
        if(this.props.open){
            if(this.props.current){
                this.show()
            }
        }else{
            if(this.locationListener){
                window.cancelAnimationFrame(this.locationListener)
                this.locationListener = null
            }
            this.ref.current.style.height = 0
            this.ref.current.style.opacity = 0
            this.timer = setTimeout(() => {
                clearTimeout(this.timer)
                this.timer = null
                window.removeEventListener('click', this.clickListener)
            }, 300)
        }
    }
    componentDidMount(){
        this.show()
        window.addEventListener('hashchange', this.remove)
    }
    componentWillUnmount(){
        window.removeEventListener('click', this.clickListener)
        window.removeEventListener('hashchange', this.remove)
    }
    render(){
        return(
            <div
                ref={ this.ref }
                className={ cssStyle.list }
                onClick={ e => { e.stopPropagation }}
            >
                <div>
                    <div className={ cssStyle.picker }>
                        <Picker type="hour" value={ this.state.hour } onChange={ (value => { this.onChange(value, 'hour') }) } />
                        <Picker type="minute" value={ this.state.minute } onChange={ (value => { this.onChange(value, 'minute') }) } />
                        <Picker type="second" value={ this.state.second } onChange={ (value => { this.onChange(value, 'second') }) } />
                        <div className={ cssStyle.currentTimeActive }></div>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'flex-end', padding: '10px 0' }}>
                        <Tag size="small" style={{ width: 'unset', height: 'unset', lineHeight: 'unset', marginRight: '10px' }} onClick={ () => { this.props.toggle(this.props.current) } }>取消</Tag>
                        <Tag type="primary" size="small" style={{ width: 'unset', height: 'unset', lineHeight: 'unset', marginRight: '10px' }} onClick={ this.confirm }>确定</Tag>
                    </div>
                </div>
            </div>
        )
    }
}

TimePickerComponent.propTypes = {
    confirm: PropTypes.func,
    toggle: PropTypes.func,
    current: PropTypes.object,
    open: PropTypes.bool,
    remove: PropTypes.func,
    value: PropTypes.string
}

class TimePicker extends React.PureComponent{
    constructor(props){
        super(props)
        this.open = false
        let time
        if(props.value){
            time = props.value.split(':')
        }else{
            time = [0, 0, 0]
        }
        this.state = {
            hour: parseInt(time[0]),
            minute: parseInt(time[1]),
            second: parseInt(time[2])
        }
        this.toggle = this.toggle.bind(this)
        this.selectRef = React.createRef()
        this.arrowRef = React.createRef()
        this.timePickerComponent = null
    }
    comfirm = (time, current) => {
        if(this.props.onChange){
            this.props.onChange(time)
        }
        this.toggle(current)
    }
    toggleCalendarComponent = (open, current) => {
        if(open){
            if(!this.timePickerComponent){
                this.timePickerComponent = document.createElement('div')
                document.body.appendChild(this.timePickerComponent)
            }
        }
        ReactDOM.render(
            <TimePickerComponent {...this.props} open={ open } current={ current } toggle={ this.toggle } confirm={ this.confirm } remove={ this.remove } />,
            this.timePickerComponent
        )
    }
    remove = () => {
        ReactDOM.unmountComponentAtNode(this.timePickerComponent)
        document.body.removeChild(this.timePickerComponent)
        this.timePickerComponent = null
    }
    toggle = current => {
        this.open = !this.open
        if(this.open){
            this.arrowRef.current.style.transform = 'translateY(-50%) rotate(180deg)'
            current.children[0].style.borderColor = '#409eff'
        }else{
            if (this.arrowRef.current) {
                this.arrowRef.current.style.transform = 'translateY(-50%) rotate(0)'
                if(this.props.invalid){
                    current.children[0].style.borderColor = 'red'
                }else{
                    current.children[0].style.borderColor = '#dcdfe6'
                }
            }
        }
        this.toggleCalendarComponent(this.open, current)
    }
    confirm = (time, current) => {
        if(this.props.onChange){
            this.props.onChange(time)
        }
        this.toggle(current)
    }
    render(){
        let invalidStyle = {}
        if(this.props.invalid){
            invalidStyle.borderColor = 'red'
        }
        return(
            <div className={ cssStyle.select }
                onClick={
                    e => {
                        if(this.open){
                            e.stopPropagation()
                        }
                        this.toggle(this.selectRef.current)
                    } 
                }
            >
                <div className={ cssStyle.selectInput } ref={ this.selectRef } style={{ ...invalidStyle }}>
                    <div style={{ cursor: 'pointer', textOverflow: 'ellipsis' }}>
                        {
                            this.props.value !== ''
                            ?
                            <span>{ this.props.value }</span>
                            :
                            <span style={{ color: '#999' }}>{ this.props.placeholder }</span>
                        }
                    </div>
                    <img ref={ this.arrowRef } src={ arrow } alt="arrow" />
                </div>
            </div>
        )
    }
}

TimePicker.propTypes = {
    onChange: PropTypes.func,
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    placeholder: PropTypes.string,
    invalid: PropTypes.bool
}

class Picker extends React.PureComponent{
    constructor(props){
        super(props)
        this.refData = {
            sliderBarMax: 0,
            listMax: 0,
            offsetHeight: 0,
            clientHeight: 245,
            itemHeight: 0
        }
        this.listRef = React.createRef()
        this.sildeBarRef = React.createRef()
        this.setPosition = this.setPosition.bind(this)
        this.requestId = null
    }
    componentDidMount(){
        let offsetHeight = this.listRef.current.offsetHeight
        this.refData.offsetHeight = offsetHeight
        let clientHeight = 245
        this.refData.clientHeight = clientHeight
        this.sildeBarRef.current.style.height = (clientHeight / offsetHeight)*100 + '%'
        let sliderBarMax = (offsetHeight - clientHeight)/clientHeight*100
        let listMax = -(offsetHeight - clientHeight)/offsetHeight*100
        this.refData.sliderBarMax = sliderBarMax
        this.refData.listMax = listMax
        this.refData.itemHeight = parseFloat(this.listRef.current.parentNode.style.lineHeight)

        let listRefTranslateY = `-${this.props.value*this.refData.itemHeight}`
        this.listRef.current.style.transform = `translateY(${listRefTranslateY}px)`
        let sildeBarRefTranslateY = this.props.value*this.refData.itemHeight/offsetHeight*Math.abs(this.refData.sliderBarMax/this.refData.listMax)
        this.sildeBarRef.current.style.transform = `translateY(${sildeBarRefTranslateY*100}%)`
    }
    onChange = (value, type, e) => {
        this.props.onChange(value, type)
        let listRefTranslateY = `-${value*this.refData.itemHeight}`
        this.listRef.current.style.transform = `translateY(${listRefTranslateY}px)`
        let sildeBarRefTranslateY = value*this.refData.itemHeight/this.refData.offsetHeight*Math.abs(this.refData.sliderBarMax/this.refData.listMax)
        this.sildeBarRef.current.style.transform = `translateY(${sildeBarRefTranslateY*100}%)`
        e.stopPropagation()
    }
    mouseOver = () => {
        var mousewheelevt = (/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel"
        if (window.attachEvent){
            this.listRef.current.attachEvent("on" + mousewheelevt, this.setPosition, { passive: false })
        }else if (window.addEventListener){
            this.listRef.current.addEventListener(mousewheelevt, this.setPosition, { passive: false })
        }
    }
    mouseOut = () => {
        this.listRef.current.removeEventListener('mousewheel', this.setPosition, { passive: false })
    }
    setPosition = e => {
        if(this.requestId){
            window.cancelAnimationFrame(this.requestId)
            this.requestId = null
        }
        this.requestId = window.requestAnimationFrame(() => {
            let listRefTranslateY = parseFloat(this.listRef.current.style.transform.split('(')[1])
            let sildeBarRefTranslateY = parseFloat(this.sildeBarRef.current.style.transform.split('(')[1])/100
            let step = this.refData.itemHeight*3
            let sildeBarStep = step/this.listRef.current.offsetHeight
            if((e.type === 'wheel' && e.deltaY > 0) || (e.type === 'DOMMouseScroll' && e.detail > 0)){
                if(parseFloat(sildeBarRefTranslateY.toFixed(4)) < parseFloat((this.refData.sliderBarMax/100).toFixed(4))){
                    listRefTranslateY -= step
                    sildeBarRefTranslateY += Math.abs(this.refData.sliderBarMax/this.refData.listMax)*sildeBarStep
                    if(sildeBarRefTranslateY > this.refData.sliderBarMax/100){
                        listRefTranslateY = this.refData.clientHeight - this.refData.offsetHeight
                        sildeBarRefTranslateY = this.refData.sliderBarMax/100
                    }
                }else{
                    return
                }
            }else{
                if(sildeBarRefTranslateY > 0){
                    listRefTranslateY += step
                    sildeBarRefTranslateY -= Math.abs(this.refData.sliderBarMax/this.refData.listMax)*sildeBarStep
                    if(sildeBarRefTranslateY < 0){
                        listRefTranslateY = 0
                        sildeBarRefTranslateY = 0
                    }
                }else{
                    return
                }
            }
            let selectIndex = Math.abs(parseInt(listRefTranslateY/this.refData.itemHeight))
            this.props.onChange(selectIndex, this.props.type)
            this.listRef.current.style.transform = `translateY(${listRefTranslateY}px)`
            this.sildeBarRef.current.style.transform = `translateY(${sildeBarRefTranslateY*100}%)`
        })
        e.preventDefault()
        e.stopPropagation()
    }
    render(){
        let list = []
        if(this.props.type === 'hour'){
            for(let i = 0; i < 24; i++){
                list.push(i)
            }
        }else{
            for(let i = 0; i < 60; i++){
                list.push(i)
            }
        }
        return(
            <div
                className={ cssStyle.timePicker }
                onMouseOver={ this.mouseOver }
                onMouseOut={ this.mouseOut }
            >
                <div
                    className={ cssStyle.content }
                    style={{ lineHeight: '35px' }}
                >
                    <ul ref={ this.listRef } style={{ transform: 'translateY(0%)' }}>
                        {
                            list.map((item, index) => {
                                return <Time key={ index } type={ this.props.type } value={ item } label={ item } active={ this.props.value === index ? true : false } onChange={ ((value, type, e) => { this.onChange(value, type, e) }) } />
                            })
                        }
                    </ul>
                </div>
                <div ref={ this.sildeBarRef } className={ cssStyle.sildeBar } style={{ transform: 'translateY(0%)' }}></div>
            </div>
        )
    }
}

Picker.propTypes = {
    type: PropTypes.string,
    onChange: PropTypes.func,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}

class Time extends React.PureComponent{
    render(){
        return(
            <li
                onClick={ (e => { this.props.onChange(this.props.value, this.props.type, e) }).bind(this) }
                style={{
                    color: this.props.active ? '#409eff' : '#606266',
                    fontWeight: this.props.active ? 'bold' : 'normal'
                }}
            >
                { this.props.label }
            </li>
        )
    }
}

Time.propTypes = {
    onChange: PropTypes.func,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    label: PropTypes.number,
    active: PropTypes.bool,
    type: PropTypes.string
}

export {
    TimePicker
}